﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Pavilion;

namespace Pavilion.GameTools
{
    public enum KeyboardType { PlayerKeyboard, ArtificialKeyboard }
    public class KeyboardTool : GameToolBase 
    {
        private static Dictionary<string, Action<KeyboardTool, object>> dataExecutionDictionary;

        public KeyboardType KeyboardType { get { return keyboardType; } set { SetKeyboardType(value); } }

        private IKeyboard keyboard;
        private KeyboardType keyboardType;
        private Dictionary<Keys, NotifiableCollection<Action>> PressedKeyActions, ReleasedKeyActions;
        private DynamicIterator<Action> actionIterator;

        public KeyboardTool(GameItem parentGameItem) : base(parentGameItem)
        {
            CreateKeyboard(KeyboardType.PlayerKeyboard.ToString());
            PressedKeyActions = new Dictionary<Keys, NotifiableCollection<Action>>();
            ReleasedKeyActions = new Dictionary<Keys, NotifiableCollection<Action>>();
            actionIterator = new DynamicIterator<Action>();

            LoadDataExecution();
        }

        public override void Dispose()
        {
            DisposeCurrentKeypoard();

            base.Dispose();
        }

        private void LoadDataExecution()
        {
            if (dataExecutionDictionary != null)
                return;

            dataExecutionDictionary = new Dictionary<string, Action<KeyboardTool, object>>();

            dataExecutionDictionary.Add("KeyboardType", (keyboardTool, value) => keyboardTool.KeyboardType = (KeyboardType)value);
        }

        public override void UpdateProperty(string propertyName, object propertyValue)
        {
            dataExecutionDictionary[propertyName].Invoke(this, propertyValue);
        }

        public void RegisterKeyPressed(Action action, params Keys[] keys)
        {
            foreach (Keys key in keys)
            {
                if (!PressedKeyActions.ContainsKey(key))
                    PressedKeyActions.Add(key, new NotifiableCollection<Action>());

                PressedKeyActions[key].Add(action);
            }
        }

        public void UnregisterKeyPressed(Action action, params Keys[] keys)
        {
            foreach (Keys key in keys)
            {
                if (PressedKeyActions.ContainsKey(key))
                {
                    PressedKeyActions[key].Remove(action);

                    if (PressedKeyActions[key].Count == 0)
                        PressedKeyActions.Remove(key);
                };
            }
        }

        public void RegisterKeyReleased(Action action, params Keys[] keys)
        {
            foreach (Keys key in keys)
            {
                if (!ReleasedKeyActions.ContainsKey(key))
                    ReleasedKeyActions.Add(key, new NotifiableCollection<Action>());

                ReleasedKeyActions[key].Add(action);
            }
        }

        public void UnregisterKeyReleased(Action action, params Keys[] keys)
        {
            foreach (Keys key in keys)
            {
                if (ReleasedKeyActions.ContainsKey(key))
                {
                    ReleasedKeyActions[key].Remove(action);

                    if (ReleasedKeyActions[key].Count == 0)
                        ReleasedKeyActions.Remove(key);
                };
            }
        }

        public bool GetKeyState(Keys key, bool isKeyDown)
        {
            return keyboard.GetKeyState(key, isKeyDown);
        }

        private void CreateKeyboard(string keyboardType)
        {
            if (keyboardType == KeyboardType.PlayerKeyboard.ToString())
                keyboard = new PlayerKeyboard();
            else if (keyboardType == KeyboardType.ArtificialKeyboard.ToString())
                keyboard = new ArtificialKeyboard();
            else
                return;

            keyboard.KeysPressed += new Action<Keys[]>(Keyboard_KeysPressed);
            keyboard.KeysReleased += new Action<Keys[]>(Keyboard_KeysReleased);
            ParentGameItem.UpdateLogic += keyboard.Update;
        }

        private void DisposeCurrentKeypoard()
        {
            if (keyboard == null)
                return;

            ParentGameItem.UpdateLogic -= keyboard.Update;
            keyboard.KeysPressed -= new Action<Keys[]>(Keyboard_KeysPressed);
            keyboard.KeysReleased -= new Action<Keys[]>(Keyboard_KeysReleased);
            keyboard = null;
        }

        private void ExecuteActions(Keys[] keys, Dictionary<Keys, NotifiableCollection<Action>> actionDictionary)
        {
            foreach (Keys key in keys)
            {
                try { actionIterator.RegisterCollection(actionDictionary[key]); } catch { continue; }

                while (actionIterator.HasMore)
                    actionIterator.Next().Invoke();
            }
        }

        private void SetKeyboardType(KeyboardType value)
        {
            if (keyboard != null && keyboardType == value)
                return;

            keyboardType = value;

            DisposeCurrentKeypoard();
            CreateKeyboard(value.ToString());
        }

        private void Keyboard_KeysReleased(Keys[] keys)
        {
            ExecuteActions(keys, ReleasedKeyActions);
        }

        private void Keyboard_KeysPressed(Keys[] keys)
        {
            ExecuteActions(keys, PressedKeyActions);
        }
    }
}
